package com.ipan.jfinal.interceptor;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.ipan.jfinal.annotation.RequiresPermissions;
import com.ipan.jfinal.annotation.RequiresRoles;
import com.ipan.jfinal.annotation.SysLog;
import com.ipan.jfinal.plugin.userinfo.UserInfo;
import com.ipan.jfinal.plugin.userinfo.UserInfoManager;
import com.ipan.kits.base.ExceptionUtil;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.render.RenderManager;

/**
 * 通用拦截器
 * 
 * 目前支持：
 * 		1 访问日志记录【前置、后置、完成、全部】
 * 		2 角色、权限拦截【前置通知】
 * 
 * 注意：多端拦截器需要自行实现，并且拦截器必须是有默认构造器的，不能带参！
 * 		
 * @author iPan
 * @version 2021-11-15
 */
public class CommonInterceptor implements Interceptor {
	
	/**
	 * 写入日志接口
	 */
	public static interface SysLogging {
		void log(Invocation inv, UserInfo userInfo, SysLog logAnn, Exception e);
//		void log(HttpServletRequest request, UserInfo userinfo, String message, SysLog.Type type, BusinessType businessType, boolean isSaveRequestData);
	}
	
	/** 系统日志记录 */
	private SysLogging sysLogger = null;
	
	public CommonInterceptor() {
	}
	
	public CommonInterceptor(SysLogging sysLogger) {
		this.sysLogger = sysLogger;
	}
	
	public void setSysLogger(SysLogging sysLogger) {
		this.sysLogger = sysLogger;
	}
	
	public SysLogging getSysLogger() {
		return sysLogger;
	}

	@Override
	public void intercept(Invocation inv) {
		Method method = inv.getMethod();
		Exception pe = null;
		if (method != null) {
			try {
				if (preHandle(method, inv)) { 	// 前置通知，一定执行；
					inv.invoke(); 				// 执行拦截器链，可能执行；
					postHandle(method, inv); 	// 后置通知，可能执行；
				} else { // 无权限访问
					inv.getController().renderError(403);
				}
			} catch (Exception e) {
				// 异常写入日志
				pe = e;
				ExceptionUtil.unchecked(e);
			} finally {
				finishHandle(method, inv, pe); 		// 完成通知，一定执行；只有完成通知可以捕获异常；
			}
		} else {
			inv.invoke();
		}
	}
	
	/**
	 * 前置通知
	 */
	protected boolean preHandle(Method method, Invocation inv) {
		HttpServletRequest request = inv.getController().getRequest();
		UserInfo userInfo = UserInfoManager.getUserInfo(request, null);
		preLogHandle(method, inv); // 日志前置通知
		if (userInfo == null) {
			return true; // 继续往下执行（不拦截，拦截交给LoginCheckInterceptor，模块各自进行配置）
		}
		
		// 角色拦截
		RequiresRoles roleCheck = method.getAnnotation(RequiresRoles.class);
		if (roleCheck != null) {
			String[] roles = roleCheck.value();
			boolean isSuccess = (roleCheck.existed()) ? UserInfoManager.hasRole(request, null, roles) : !UserInfoManager.hasRole(request, null, roles);
			if(!isSuccess) {
				return false;
			}
		}
		
		// 权限拦截
		RequiresPermissions permissionCheck = method.getAnnotation(RequiresPermissions.class);
		if (permissionCheck != null) {
			String[] permissions = permissionCheck.value();
			boolean isSuccess = (permissionCheck.existed()) ? UserInfoManager.hasPermission(request, null, permissions)
															: !UserInfoManager.hasPermission(request, null, permissions);
			if(!isSuccess) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * 后置通知
	 */
	protected void postHandle(Method method, Invocation inv) {
		postLogHandle(method, inv);
	}
	
	/**
	 * 完成通知
	 */
	protected void finishHandle(Method method, Invocation inv, Exception e) {
		finishLogHandle(method, inv, e);
	}
	
	// 解析模板字符串
	protected String parseMsg(String msg, Map<String, Object> data) {
		String resultMsg = RenderManager.me().getEngine().getTemplateByString(msg).renderToString(data);
		return resultMsg;
	}
	
	protected Map<String, Object> getParaMap(HttpServletRequest request) {
		Map<String, Object> newData = new HashMap<String, Object>();
		Map<String, String[]> data = request.getParameterMap();
		Iterator<String> keys = data.keySet().iterator();
		while (keys.hasNext()) {
			String key = keys.next();
			String[] values = data.get(key);
			if (values != null) {
				if (values.length > 1) {
					newData.put(key, Arrays.asList(values).toString());
				} else {
					newData.put(key, values[0]);
				}
			}
		}
		return newData;
	}
	
	// 处理日志前置通知
	protected void preLogHandle(Method method, Invocation inv) {
		HttpServletRequest request = inv.getController().getRequest();
		UserInfo userInfo = UserInfoManager.getUserInfo(request, null);
		SysLog logAnn = method.getAnnotation(SysLog.class);
		if (userInfo == null || logAnn == null) {
			return ;
		}
		
		SysLog.Pattern pattern = logAnn.pattern();
		if (SysLog.Pattern.FRONT.equals(pattern) || SysLog.Pattern.ALL.equals(pattern)) {
			sysLogger.log(inv, userInfo, logAnn, null);
//			String message = logAnn.preMessage();
//			Map<String, Object> data = getParaMap(request);
////			Map<String, Object> data2 = ObjectUtils.clone(data); // 默认浅拷贝
//			data.put("_user", userInfo); // 加入登录用户信息，登出之后写日志还能使用；
//			String result = parseMsg(message, data);
//			sysLogger.log(request, userInfo, result, logAnn.type(), logAnn.businessType(), logAnn.isSaveRequestData());
		}
	}
	// 处理日志后置通知
	protected void postLogHandle(Method method, Invocation inv) {
		HttpServletRequest request = inv.getController().getRequest();
		UserInfo userInfo = UserInfoManager.getUserInfo(request, null);
		if (userInfo == null) { // 登入/登出
			userInfo = (UserInfo) request.getAttribute("_user");
		}
		SysLog logAnn = method.getAnnotation(SysLog.class);
		if (userInfo == null || logAnn == null) {
			return ;
		}
		
		// 系统日志——后置通知处理
		SysLog.Pattern pattern = logAnn.pattern();
		if (SysLog.Pattern.AFTER.equals(pattern) || SysLog.Pattern.ALL.equals(pattern)) {
			sysLogger.log(inv, userInfo, logAnn, null);
//			String message = logAnn.postMessage();
//			Map<String, Object> data = getParaMap(request);
//			data.put("_user", userInfo);
//			String result = parseMsg(message, data);
//			sysLogger.log(request, userInfo, result, logAnn.type(), logAnn.businessType(), logAnn.isSaveRequestData());
		}
	}
	
	protected void finishLogHandle(Method method, Invocation inv, Exception e) {
		HttpServletRequest request = inv.getController().getRequest();
		UserInfo userInfo = UserInfoManager.getUserInfo(request, null);
		if (userInfo == null) { // 登入/登出
			userInfo = (UserInfo) request.getAttribute("_user");
		}
		SysLog logAnn = method.getAnnotation(SysLog.class);
		if (userInfo == null || logAnn == null) {
			return ;
		}
		
		// 系统日志——完成通知处理
		SysLog.Pattern pattern = logAnn.pattern();
		if (SysLog.Pattern.FINISH.equals(pattern) || SysLog.Pattern.ALL.equals(pattern)) {
			sysLogger.log(inv, userInfo, logAnn, e);
		}
	}

}
